/*
 *  testTrans.cpp
 *  kangmodb
 *
 *  Created by 강모 김 on 11. 4. 11..
 *  Copyright 2011 강모소프트. All rights reserved.
 *
 */

#include "gtest/gtest.h"

#include "kangmodb.h"
#include "testCommon.h"

/** @brief Test using transaction savepoint, rollback, commit
 */
class TransTest : public testing::Test {
protected:  // You should make the members protected s.t. they can be
	// accessed from sub-classes.
	
	// virtual void SetUp() will be called before each test is run.  You
	// should define it if you need to initialize the varaibles.
	// Otherwise, this can be skipped.
	virtual void SetUp() {
		ASSERT_TRUE( setup_cursor( &db_, &tx_, &tableId_, &cursor_ ) == 0 );
	}
	
	// virtual void TearDown() will be called after each test is run.
	// You should define it if there is cleanup work to do.  Otherwise,
	// you don't have to provide it.
	//
	virtual void TearDown() {
		// close table succeeds
		ASSERT_TRUE( teardown_cursor( &db_, &tx_, &tableId_, &cursor_ ) == 0 );
	}
protected:
	stgKangmoDB * db_;
	stgTransaction * tx_;
	stgTableId tableId_;
	stgTableCursor * cursor_;
};


TEST_F(TransTest, DISABLED_TransSavepoint) {
	int sp;
	// Begin transaction is already done by setup
								
	// insert 1st key
	ASSERT_TRUE( cursor_->insertRecord( stgKey("1st"), KD_STR_DATA("data-1st") ) == 0 );

    //** TEST : insert, rollback to savepoint 
	// get the savepoint
	ASSERT_TRUE( db_->newSavepoint( tx_, & sp ) == 0 );
    {
		// insert a key
		ASSERT_TRUE( cursor_->insertRecord( stgKey("2nd"), KD_STR_DATA("data-2nd") ) == 0 );
    }
	
	// rollback to the savepoint
	ASSERT_TRUE( db_->rollbackTransaction( tx_, sp ) == 0 );		

	// seek 1st key - should be found
	verify_key_value(cursor_, "1st", "data-1st");
	// seek 2nd key - should not be found
	verify_key_value(cursor_, "2nd", NULL);

	// insert 2nd key
	ASSERT_TRUE( cursor_->insertRecord( stgKey("2nd"), KD_STR_DATA("data-2nd") ) == 0 );

    //** TEST : update, rollback to savepoint 
	// get the savepoint
	ASSERT_TRUE( db_->newSavepoint( tx_, &sp ) == 0 );
    {
		// update 2nd key
		ASSERT_TRUE( cursor_->updateRecord( stgKey("2nd"), KD_STR_DATA("data-2nd-updated") ) == 0 );

		// seek 2nd key - should have the updated value
		verify_key_value(cursor_, "2nd", "data-2nd-updated");
    }

	// rollback to the savepoint
	ASSERT_TRUE( db_->rollbackTransaction( tx_, sp ) == 0 );		

	// seek 1st key - should be found
	verify_key_value(cursor_, "1st", "data-1st");
	// seek 2nd key - should have the original value (before the update)
	verify_key_value(cursor_, "2nd", "data-2nd");
	
    //** TEST : delete, rollback to savepoint 
	// get the savepoint
	ASSERT_TRUE( db_->newSavepoint( tx_, &sp ) == 0 );
    {
		// update 2nd key
		ASSERT_TRUE( cursor_->deleteRecord( stgKey("2nd") ) == 0 );

		// seek 2nd key - should not exist
		verify_key_value(cursor_, "2nd", NULL);
    }

	// rollback to the savepoint
	ASSERT_TRUE( db_->rollbackTransaction( tx_, sp ) == 0 );		

	// seek 1st key - should be found
	verify_key_value(cursor_, "1st", "data-1st");
	// seek 2nd key - should have the original value (before the deletion)
	verify_key_value(cursor_, "2nd", "data-2nd");

	// End transaction will be done by teardown
}

// Test the life cycle of a transaction
// Begin trans -> MIXED( Open/Close cursor, newSavepoint, Rollback to savepoint, Total rollback, Commit ) -> End trans 
// FAQ> No need to close the cursor before transaction commit?
//      No support for unique constraint or foreign key checks. One way to implement these checks is to check them within "Cursor Close" interface.
//      But they are not supported, so no need to close cursors before the transaction commit.
TEST_F(TransTest, DISABLED_TransLifeCycle) {
	stgSavepointId sp;

	// Begin transaction is already done by setup
	// Open cursor_ is already done by setup

	// success : close the cursor. 
	ASSERT_TRUE( db_->closeTable( tx_, cursor_) == 0 );

	// See if interfaces succeed even though the cursor is not open.
	{
		// success : Alloc a new savepoint 
		ASSERT_TRUE( db_->newSavepoint( tx_, &sp) == 0 );

		// success : Rollback to savepoint 
		ASSERT_TRUE( db_->rollbackTransaction( tx_, sp) == 0 );

		// success : Total rollback to savepoint 
		ASSERT_TRUE( db_->rollbackTransaction( tx_ ) == 0 );

		// success : commit 
		ASSERT_TRUE( db_->commitTransaction( tx_ ) == 0 );
	}

	// success : Alloc a new savepoint before opening a cursor
	ASSERT_TRUE( db_->newSavepoint( tx_, &sp) == 0 );
	
	// Now the cursor is open agin.
	// success : open the cursor. 
	ASSERT_TRUE( db_->openTable( tx_, tableId_, & cursor_) == 0 );

	// insert 1st key
	ASSERT_TRUE( cursor_->insertRecord( stgKey("1st"), KD_STR_DATA("data-1st") ) == 0 );

	// seek 1st key - should be found
	verify_key_value(cursor_, "1st", "data-1st");

	// success : Rollback to the point right before the cursor was opened.
	ASSERT_TRUE( db_->newSavepoint( tx_, &sp) == 0 );

	// seek 1st key - not found, because it was rolled back.
	verify_key_value(cursor_, "1st", NULL);

	// "TransBasic" test case has executing commit/rollback/savepoint while the cursor is open. So we omit them here. 

	// Close cursor_ will be done by teardown
	// End transaction will be done by teardown
}

/** @brief Test the basic feature of the transaction; commit and rollback.
 */ 
TEST_F(TransTest, DISABLED_TransBasic) {
	// Begin transaction is already done by setup
	
	// seek 1st key - should not be found
	verify_key_value(cursor_, "1st", NULL);
							
	// insert 1st key
	ASSERT_TRUE( cursor_->insertRecord( stgKey("1st"), KD_STR_DATA("data-1st") ) == 0 );

	// seek 1st key - should be found
	verify_key_value(cursor_, "1st", "data-1st");
	
	// commit the transaction
	ASSERT_TRUE( db_->commitTransaction(tx_) == 0 );

	// seek 1st key - should be found
	verify_key_value(cursor_, "1st", "data-1st");

	// insert 2nd key
	ASSERT_TRUE( cursor_->insertRecord( stgKey("2nd"), KD_STR_DATA("data-2nd") ) == 0 );

	// seek 2nd key - should be found
	verify_key_value(cursor_, "2nd", "data-2nd");

	// rollback the transaction
	ASSERT_TRUE( db_->rollbackTransaction(tx_) == 0 );

	// seek 1st key - should still exist, because it was committed
	verify_key_value(cursor_, "1st", "data-1st");
	
	// seek 2nd key - should not be found
	verify_key_value(cursor_, "2nd", NULL);
	
	// End transaction will be done by teardown
}